﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Pahan.McuFusion.Common;
using Pahan.McuFusion.Files;
using Pahan.McuFusion.UI.Controls;
using Pahan.McuFusion.UI.Events;
using Pahan.McuFusion.UI.Interfaces;
using Pahan.McuFusion.UI.RecentFiles;
using Pahan.McuFusion.UI.Workspaces;

namespace Pahan.McuFusion.UI.Views
{
    public partial class MainView : Form, IMainView
    {
        public MainView()
        {
            InitializeComponent();
            ribbon.OrbDropDown.MenuItems.Insert(0, new RibbonSeparator("Workspace"));

            if (Environment.OSVersion.Platform == PlatformID.Win32NT ||
                Environment.OSVersion.Platform == PlatformID.Win32Windows)
            {
                this.Icon = Properties.Resources.McuFusionIcon;
            }
            else
            {
                ribbon.OrbVisible = false;
            }
        }

        #region Overrides of Form

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            var handler = Loaded;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }            
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Form.FormClosing"></see> event.
        /// </summary>
        /// <param name="e">A <see cref="TValue:System.Windows.Forms.FormClosingEventArgs"></see> that contains the event data. </param>
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            var handler = this.Closing;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region Implementation of IMainView

        public event EventHandler NewWorkspaceRequested;
        public event EventHandler SaveWorkspaceRequested;
        public event EventHandler SaveWorkspaceAsRequested;
        public event OpenWorkspaceEventHandler OpenWorkspaceRequested;
        public event MemoryOperationEventHandler ReadMemoryRequested;
        public event MemoryOperationEventHandler OpenFileRequested;
        public event MemoryOperationEventHandler SaveFileRequested;
        public event MemoryOperationEventHandler WriteMemoryRequested;
        public event MemoryOperationEventHandler EraseMemoryRequested;
        public event EventHandler RunScriptRequested;
        public event EventHandler Loaded;
        public new event CancelEventHandler Closing;

        /// <summary>
        /// Shows the workspace.
        /// </summary>
        public void ShowWorkspace(Workspace workspace)
        {
            if (workspaceBox.Workspace != null)
            {
                workspaceBox.Workspace.ModifiedChanged -= OnWorkspaceModified;
            }
            workspaceBox.Workspace = workspace;
            ribbon.Tabs.Clear();
            ribbon.Tabs.Add(rtHomeTab);
            if (workspace != null)
            {
                workspace.ModifiedChanged += OnWorkspaceModified;
                this.Text = String.Format("{0}{1} - MCU Fusion",
                    workspace.Name,
                    workspace.Modified ? "*" : String.Empty);
                PrepareMenu(workspace.DeviceInfo.Memory.Count > 0);
                foreach (var memoryClass in workspace.DeviceInfo.Memory)
                {
                    IMemoryView view = new MemoryView(memoryClass);
                    view.EraseRequsted += OnEraseRequested;
                    view.ReadRequested += OnReadRequested;
                    view.ProgramRequested += OnProgramRequested;
                    var memory = memoryClass;
                    view.OpenFileRequested += (s, e) => OnMemoryOpenClick(s, new MemoryOperationEventArgs(memory));
                    view.SaveFileAsRequested += (s, e) => OnMemorySaveClick(s, new MemoryOperationEventArgs(memory));
                    var tab = (RibbonTab)view.GetRibbonTab();
                    ribbon.Tabs.Add(tab);
                    AddMenuItem(memoryClass);
                }
            }
        }

        /// <summary>
        /// Asks the user to select the workspace file to open.
        /// </summary>
        /// <returns>The <see cref="FileInfo"/> object for the file selected by the user 
        /// or null if the user has cancelled the operation.</returns>
        public FileInfo GetOpenWorkspaceName()
        {
            var dlg = new OpenFileDialog()
            {
                CheckFileExists = true,
                CheckPathExists = true,
                DereferenceLinks = true,
                Filter = workspaceFilter,
                Multiselect = false
            };
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                return new FileInfo(dlg.FileName);
            }
            return null;
        }

        /// <summary>
        /// Asks the user to select the file name to save the workspace.
        /// </summary>
        /// <returns>The <see cref="FileInfo"/> object for the file selected by the user 
        /// or null if the user has cancelled the operation.</returns>
        public FileInfo GetSaveWorkspaceName()
        {
            var dlg = new SaveFileDialog()
            {
                AddExtension = true,
                DefaultExt = Workspace.WorkspaceExtension,
                Filter = workspaceFilter,
                OverwritePrompt = true,
                ValidateNames = true
            };
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                return new FileInfo(dlg.FileName);
            }
            return null;
        }

        private void OnProgramRequested(object sender, EventArgs e)
        {
            var view = (IMemoryView)sender;
            var handler = WriteMemoryRequested;
            if (handler != null)
            {
                handler(this, new MemoryOperationEventArgs(view.MemoryClass));
            }
        }

        private void OnReadRequested(object sender, EventArgs e)
        {
            var view = (IMemoryView)sender;
            var handler = ReadMemoryRequested;
            if (handler != null)
            {
                handler(this, new MemoryOperationEventArgs(view.MemoryClass));
            }
        }

        private void OnEraseRequested(object sender, EventArgs e)
        {
            var view = (IMemoryView)sender;
            var handler = EraseMemoryRequested;
            if (handler != null)
            {
                handler(this, new MemoryOperationEventArgs(view.MemoryClass));
            }
        }

        /// <summary>
        /// Shows the content of memory.
        /// </summary>
        public void ShowMemory(IList<byte> bytes, MemoryClassInfo memoryClass)
        {
            foreach (var tab in ribbon.Tabs)
            {
                var view = tab.Tag as IMemoryView;
                if (view != null && view.MemoryClass == memoryClass)
                {
                    view.Data = bytes;
                    return;
                }
            }
        }

        /// <summary>
        /// Gets the content of memory.
        /// </summary>
        public IList<byte> GetMemory(MemoryClassInfo memoryClass)
        {
            foreach (var tab in ribbon.Tabs)
            {
                var view = tab.Tag as IMemoryView;
                if (view != null && view.MemoryClass == memoryClass)
                {
                    return view.Data;
                }
            }
            throw new InvalidOperationException();
        }

        /// <summary>
        /// Asks the user to select the file to open.
        /// </summary>
        /// <returns>The <see cref="FileInfo"/> object for the file selected by the user 
        /// or null if the user has cancelled the operation.</returns>
        public FileInfo GetOpenFileName(IEnumerable<FileTypeInfo> fileTypes)
        {
            var dlg = new OpenFileDialog()
            {
                CheckFileExists = true,
                CheckPathExists = true,
                DereferenceLinks = true,
                Filter = CreateFilter(fileTypes),
                Multiselect = false
            };
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                return new FileInfo(dlg.FileName);
            }
            return null;
        }

        /// <summary>
        /// Asks the user to select the file to open.
        /// </summary>
        /// <param name="fileTypes">The dictionary containing the file type display names as keys
        /// and lists of possible extensions as values.</param>
        public FileInfo GetOpenFileName(IEnumerable<KeyValuePair<string, IEnumerable<string>>> fileTypes)
        {
            var dlg = new OpenFileDialog()
            {
                CheckFileExists = true,
                CheckPathExists = true,
                DereferenceLinks = true,
                Filter = CreateFilter(fileTypes),
                Multiselect = false
            };
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                return new FileInfo(dlg.FileName);
            }
            return null;
        }

        /// <summary>
        /// Asks the user to select the file name to save.
        /// </summary>
        /// <returns>The <see cref="FileInfo"/> object for the file selected by the user 
        /// or null if the user has cancelled the operation.</returns>
        public FileInfo GetSaveFileName(IEnumerable<FileTypeInfo> fileTypes)
        {
            var dlg = new SaveFileDialog()
            {
                AddExtension = true,
                Filter = CreateFilter(fileTypes),
                OverwritePrompt = true,
                ValidateNames = true
            };
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                return new FileInfo(dlg.FileName);
            }
            return null;
        }

        /// <summary>
        /// Refreshes the list of recent files.
        /// </summary>
        public void RefreshRecentFilesList(IEnumerable<RecentFile> files)
        {
            // Clear items.
            foreach (var item in ribbon.OrbDropDown.RecentItems.OfType<RecentWorkspaceItem>())
            {
                item.Click -= OnOpenRecentWorkspace;
            }
            ribbon.OrbDropDown.RecentItems.Clear();

            ribbon.OrbDropDown.RecentItems.Add(new RibbonSeparator("Recent Workspaces"));
            foreach (var file in files)
            {
                var item = new RecentWorkspaceItem(file);
                item.Click += OnOpenRecentWorkspace;
                ribbon.OrbDropDown.RecentItems.Add(item);
            }
        }

        /// <summary>
        /// Asks the you whether he wants to save changes and close the workspace.
        /// </summary>
        /// <returns>
        /// True if the user wants to save changes; 
        /// false if he wants to discard changes;
        /// null if he does not want to close the workspace.
        /// </returns>
        public bool? ConfirmCloseWorkspace()
        {
            var result = MessageBox.Show(this,
                "Save changes to the current workspace?",
                "Confirmation",
                MessageBoxButtons.YesNoCancel,
                MessageBoxIcon.Question);
            switch (result)
            {
                case DialogResult.Yes:
                    return true;
                case DialogResult.No:
                    return false;
                default:
                    return null;
            }
        }

        /// <summary>
        /// Shows the error message.
        /// </summary>
        public void ShowError(string message)
        {
            MessageBox.Show(this, message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        #endregion

        #region Event Handler

        private void ribbon_ActiveTabChanged(Object sender, EventArgs e)
        {
            if (ribbon.ActiveTab == rtHomeTab)
            {
                SetContent(workspaceBox);
            }
            else if (ribbon.ActiveTab.Tag is IMemoryView)
            {
                var control = ribbon.ActiveTab.Tag as Control;
                SetContent(control);
            }
        }

        private void OnOpenClicked(Object sender, EventArgs e)
        {
            var view = ribbon.ActiveTab.Tag as IMemoryView;
            if (view != null)
            {
                OnMemoryOpenClick(this, new MemoryOperationEventArgs(view.MemoryClass));
            }
            else
            {
                OnOpenWorkspaceClick(this, e);
            }
        }

        private void OnSaveClicked(object sender, EventArgs e)
        {
            var view = ribbon.ActiveTab.Tag as IMemoryView;
            if (view != null)
            {
                OnMemorySaveClick(this, new MemoryOperationEventArgs(view.MemoryClass));
            }
            else
            {
                OnSaveWorkspaceClick(this, e);
            }
        }

        private void OnMemoryOpenClick(Object sender, MemoryOperationEventArgs e)
        {
            var handler = OpenFileRequested;
            if (handler != null)
            {
                try
                {
                    handler(this, e);
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        private void OnMemorySaveClick(Object sender, MemoryOperationEventArgs e)
        {
            var handler = SaveFileRequested;
            if (handler != null)
            {
                try
                {
                    handler(this, e);
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        private void OnOpenWorkspaceClick(Object sender, EventArgs e)
        {
            var handler = OpenWorkspaceRequested;
            if (handler != null)
            {
                try
                {
                    handler(this, new OpenWorkspaceEventArgs());
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        private void OnSaveWorkspaceClick(Object sender, EventArgs e)
        {
            var handler = SaveWorkspaceRequested;
            if (handler != null)
            {
                try
                {
                    handler(this, e);
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        private void OnSaveWorkspaceAsClick(Object sender, EventArgs e)
        {
            var handler = SaveWorkspaceAsRequested;
            if (handler != null)
            {
                try
                {
                    handler(this, e);
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        private void OnNewWorkspaceClick(Object sender, EventArgs e)
        {
            var handler = NewWorkspaceRequested;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        private void rbobExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void OnWorkspaceModified(object sender, EventArgs e)
        {
            var workspace = workspaceBox.Workspace;
            this.Text = String.Format("{0}{1} - MCU Fusion",
                    workspace.Name,
                    workspace.Modified ? "*" : String.Empty);
        }


        private void OnOpenRecentWorkspace(object sender, EventArgs e)
        {
            // TODO: Create more elegant solution.
            var item = (RibbonOrbRecentItem)sender;
            var handler = OpenWorkspaceRequested;
            if (handler != null)
            {
                try
                {
                    handler(this, new OpenWorkspaceEventArgs(new FileInfo(item.ToolTip)));
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        private void OnRunScriptClick(object sender, EventArgs e)
        {
            var handler = RunScriptRequested;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void OnHelp(object sender, EventArgs e)
        {
            MessageBox.Show(this, "Help file is not installed.", "MCU Fusion Help", 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        #endregion

        #region Private Members

        private void SetContent(Control control)
        {
            control.Dock = DockStyle.Fill;
            pTabContentHost.Controls.Clear();
            pTabContentHost.Controls.Add(control);
        }

        private void HandleException(Exception ex)
        {
            ShowError(ex.Message);
        }

        private String CreateFilter(IEnumerable<FileTypeInfo> fileTypes)
        {
            StringBuilder builder = new StringBuilder();
            foreach (FileTypeInfo type in fileTypes)
            {
                builder.AppendFormat("{0} (*.{1})|*.{1}|", type.Description, type.Extension);
            }
            if (builder.Length > 0)
            {
                builder.Remove(builder.Length - 1, 1);
            }
            return builder.ToString();
        }

        private String CreateFilter(IEnumerable<KeyValuePair<string, IEnumerable<string>>> fileTypes)
        {
            StringBuilder builder = new StringBuilder();
            foreach (var pair in fileTypes)
            {
                StringBuilder extBuilder = new StringBuilder();
                foreach (var ext in pair.Value)
                {
                    if (String.IsNullOrEmpty(ext)) continue;
                    extBuilder.Append(ext[0] == '.' ? "*" : "*.");
                    extBuilder.Append(ext);
                    extBuilder.Append(";");
                }
                if (extBuilder.Length > 0)
                {
                    extBuilder.Remove(extBuilder.Length - 1, 1);
                }
                builder.AppendFormat("{0} ({1})|{1}|", pair.Key, extBuilder);
            }
            if (builder.Length > 0)
            {
                builder.Remove(builder.Length - 1, 1);
            }
            return builder.ToString();
        }

        private void PrepareMenu(bool separatorVisible)
        {
            var items = ribbon.OrbDropDown.MenuItems;
            items.ForEach(item =>
            {
                var memoryItem = item as MemoryRibbonOrbMenuItem;
                if (memoryItem != null)
                {
                    memoryItem.OpenFileRequested -= OnMemoryOpenClick;
                    memoryItem.SaveFileRequested -= OnMemorySaveClick;
                }
            });
            items.RemoveAll(
                item => item is MemoryRibbonOrbMenuItem);
            if (separatorVisible)
            {
                if (!(items[items.Count - 1] is RibbonSeparator))
                {
                    items.Add(new RibbonSeparator("Memory"));
                }
            }
        }

        private void AddMenuItem(MemoryClassInfo memoryClass)
        {
            var item = new MemoryRibbonOrbMenuItem(memoryClass);
            item.OpenFileRequested += OnMemoryOpenClick;
            item.SaveFileRequested += OnMemorySaveClick;
            ribbon.OrbDropDown.MenuItems.Add(item);
        }

        private readonly String workspaceFilter = String.Format(
            "Workspaces (*.{0})|*.{0}", Workspace.WorkspaceExtension);

        #endregion

    }
}
